package com.retry.task.admin.biz.service;

import com.google.common.collect.Lists;
import com.retry.task.admin.dal.mapper.RetryTaskLogMapper;
import com.retry.task.admin.dal.mapper.RetryTaskMapper;
import com.retry.task.admin.dal.model.RetryTaskDO;
import com.retry.task.admin.dal.model.RetryTaskLogDO;
import com.retry.task.admin.dal.model.query.RetryTaskLogQuery;
import com.retry.task.admin.utils.NextExecuteTimeUtil;
import com.retry.task.core.constants.RetryTaskStatusEnum;
import com.retry.task.core.model.AttributeModel;
import com.retry.task.core.model.RetryTaskDTO;
import com.retry.task.core.model.RetryTaskLogDTO;
import com.retry.task.core.model.base.PageResult;
import com.retry.task.core.model.base.Result;
import com.retry.task.core.utils.CommonExecuteUtils;
import com.retry.task.core.utils.GsonTool;
import com.retry.task.core.utils.IpUtils;
import com.retry.task.sequence.Sequence;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author gao.gwq
 * @version 1.0
 * @date 2022/5/6  12:13
 * @Description TODO
 */
@Service
public class RetryTaskLogBizService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryTaskBizService.class);
    @Autowired
    private RetryTaskMapper retryTaskMapper;

    @Autowired
    private Sequence retryTaskLogSequence;

    @Autowired
    private RetryTaskLogMapper retryTaskLogMapper;

    @Transactional(transactionManager = "retryTaskTransactionManager",
            rollbackFor = {Exception.class, RuntimeException.class})
    public void updateTaskAndCreateLog(RetryTaskDTO retryTaskDTO, String traceId, Long logId) {
        RetryTaskDO retryTaskDO = new RetryTaskDO();
        retryTaskDO.setId(retryTaskDTO.getId());
        retryTaskDO.setCurrentLogId(logId);
        retryTaskDO.setStatus(RetryTaskStatusEnum.PROGRESS.getCode());
        AttributeModel attributeModel = retryTaskDTO.getAttributeModel();
        if (attributeModel != null && StringUtils.isEmpty(attributeModel.getOriginTraceId())) {
            attributeModel.setOriginTraceId(traceId);
            retryTaskDO.setAttribute(GsonTool.toJsonString(attributeModel));
        }
        retryTaskMapper.updateByPrimaryKey(retryTaskDO);
        RetryTaskLogDO retryTaskLogDTO = buildRetryTaskLogDTO(retryTaskDTO, traceId, logId);
        retryTaskLogMapper.insert(retryTaskLogDTO);
    }

    private RetryTaskLogDO buildRetryTaskLogDTO(RetryTaskDTO retryTaskDTO, String traceId, Long logId) {
        RetryTaskLogDO retryTaskLogDO = new RetryTaskLogDO();
        retryTaskLogDO.setTaskId(retryTaskDTO.getId());
        retryTaskLogDO.setIsTest(retryTaskDTO.getIsTest());
        retryTaskLogDO.setStatus(RetryTaskStatusEnum.TO_START.getCode());
        retryTaskLogDO.setTaskName(retryTaskDTO.getTaskName());
        retryTaskLogDO.setIsTest(retryTaskDTO.getIsTest());
        retryTaskLogDO.setProjectName(retryTaskDTO.getProjectName());
        retryTaskLogDO.setPreStartTime(retryTaskDTO.getNextPlanTime());
        retryTaskLogDO.setId(logId);
        retryTaskLogDO.setTraceId(traceId);
        return retryTaskLogDO;
    }

    @Transactional(value = "retryTaskTransactionManager", rollbackFor = {Exception.class, RuntimeException.class})
    public Result<RetryTaskLogDTO> updateRetryTaskEndTimeLog(RetryTaskLogDTO retryTaskLogDTO) {
        if (retryTaskLogDTO.getTaskId() == null) {
            return Result.getFail("100", "taskId is null");
        }
        if (retryTaskLogDTO.getId() == null) {
            return Result.getFail("100", "id is null");
        }
        RetryTaskDO retryTaskPO = retryTaskMapper.queryByPrimaryKey(retryTaskLogDTO.getTaskId());
        if (retryTaskPO == null) {
            return Result.getFail("100", "can not find task by taskId");
        }
        RetryTaskLogDO retryTaskLogPO = new RetryTaskLogDO();
        BeanUtils.copyProperties(retryTaskLogDTO, retryTaskLogPO);
        retryTaskLogMapper.updateByPrimaryKey(retryTaskLogPO);
        //如果执行成功或者已经没有执行次数则更新任务的状态
        if (retryTaskLogDTO.getStatus() == RetryTaskStatusEnum.SUCESS.getCode() ||
                retryTaskPO.getRetryNum() == 0) {
            retryTaskPO.setStatus(retryTaskLogDTO.getStatus());
        }
        //如果次数大于0,且执行失败，任务变更为待执行
        if (retryTaskPO.getRetryNum() > 0 && retryTaskLogDTO.getStatus() != RetryTaskStatusEnum.SUCESS.getCode()) {
            retryTaskPO.setStatus(RetryTaskStatusEnum.TO_START.getCode());
        }

        retryTaskMapper.updateByPrimaryKey(retryTaskPO);
        return Result.getSuccess(retryTaskLogDTO);
    }

    @Transactional(value = "retryTaskTransactionManager", rollbackFor = {Exception.class, RuntimeException.class})
    public Result<RetryTaskLogDTO> updateLogStartTimeAndTaskStatus(RetryTaskLogDTO retryTaskLogDTO) {
        if (retryTaskLogDTO.getTaskId() == null) {
            return Result.getFail("100", "taskId is null");
        }
        if (retryTaskLogDTO.getId() == null) {
            return Result.getFail("100", "id is null");

        }
        RetryTaskDO retryTaskPO = retryTaskMapper.queryByPrimaryKey(retryTaskLogDTO.getTaskId());
        if (retryTaskPO == null) {
            return Result.getFail("100", "can not find task by taskId");
        }
        RetryTaskLogDO logDTO = retryTaskLogMapper.queryByPrimaryKey(retryTaskLogDTO.getId());
        if (logDTO != null && logDTO.getStartTime() != null
                && logDTO.getStatus() == RetryTaskStatusEnum.PROGRESS.getCode()) {
            LOGGER.warn("[RetryTaskLogOperateServiceImpl-createRetryTaskLog],消息已经消费一次,{}",
                    GsonTool.toJsonString(retryTaskLogDTO));
            return Result.getFail("200", "消息已经消费一次");
        }
        retryTaskPO.setStatus(RetryTaskStatusEnum.PROGRESS.getCode());
        retryTaskPO.setRetryNum(retryTaskPO.getRetryNum() - 1);
        RetryTaskDTO retryTaskDTO = RetryTaskDTO.createBuilder().build();
        BeanUtils.copyProperties(retryTaskPO, retryTaskDTO);
        Date oldPlanTime = retryTaskPO.getNextPlanTime();
        Date nextExecuteTime = null;
        try {
            nextExecuteTime = NextExecuteTimeUtil.generateFirstTime(retryTaskDTO, oldPlanTime);
        } catch (Exception ex) {
            LOGGER.error("cron expression is not right {}", ex.getMessage(), ex);
            return Result.getFail("100", "cron expression is not right," + ex.getMessage());
        }
        retryTaskPO.setNextPlanTime(nextExecuteTime);
        retryTaskMapper.updateByPrimaryKey(retryTaskPO);
        RetryTaskLogDO retryTaskLogPO = buildRetryLogPO(retryTaskPO, retryTaskLogDTO.getId(),
                retryTaskLogDTO.getStatus(), null);
        retryTaskLogPO.setExecuteIp(IpUtils.getIp());
        retryTaskLogPO.setTraceId(retryTaskLogDTO.getTraceId());
        retryTaskLogPO.setStartTime(retryTaskLogDTO.getStartTime());
        retryTaskLogMapper.updateByPrimaryKey(retryTaskLogPO);
        BeanUtils.copyProperties(retryTaskLogPO, retryTaskLogDTO);
        return Result.getSuccess(retryTaskLogDTO);
    }

    private RetryTaskLogDO buildRetryLogPO(RetryTaskDO retryTaskPO,
                                           Long logId, int status,
                                           Date oldPrePlaneTime) {
        RetryTaskLogDO retryTaskLogPO = new RetryTaskLogDO();
        BeanUtils.copyProperties(retryTaskPO, retryTaskLogPO);
        retryTaskLogPO.setTaskId(retryTaskPO.getId());
        if (oldPrePlaneTime != null) {
            retryTaskLogPO.setPreStartTime(oldPrePlaneTime);
        }
        retryTaskLogPO.setStatus(status);
        if (logId == null) {
            logId = retryTaskLogSequence.nextValue();
        }
        retryTaskLogPO.setId(logId);
        return retryTaskLogPO;
    }

    public PageResult<List<RetryTaskLogDTO>> queryRetryTaskLog(RetryTaskLogQuery query) {
        if (query.getPageSize() > 300) {
            return PageResult.getFail("100", "pageSize  is bigger than 300");
        }
        Map<String, Object> params = CommonExecuteUtils.beanToMap(query, false);
        params.put("offset", (query.getPageNum() - 1) * query.getPageSize());
        params.put("limit", query.getPageSize());
        int count = retryTaskLogMapper.countByCondition(params);
        PageResult pageResultDTO = PageResult.getSuccess(null);
        pageResultDTO.setTotalCount(count);
        if (count == 0) {
            return pageResultDTO;
        }
        List<RetryTaskLogDO> poList = retryTaskLogMapper.queryByCondition(params);
        List<RetryTaskLogDTO> dtoList = Lists.newArrayList();
        for (RetryTaskLogDO po : poList) {
            RetryTaskLogDTO retryTaskDTO = new RetryTaskLogDTO();
            BeanUtils.copyProperties(po, retryTaskDTO);
            dtoList.add(retryTaskDTO);
        }
        pageResultDTO.setData(dtoList);
        return pageResultDTO;
    }

    public Result<Integer> updateRetryTaskLogById(RetryTaskLogDTO retryTaskLogDTO) {
        if (retryTaskLogDTO.getId() == null) {
            return Result.getFail("100", "id is null");
        }
        RetryTaskLogDO retryTaskLogDO = new RetryTaskLogDO();
        BeanUtils.copyProperties(retryTaskLogDTO, retryTaskLogDO);
        return Result.getSuccess(retryTaskLogMapper.updateByPrimaryKey(retryTaskLogDO));
    }
}
